﻿/*
 * Simple class for representing tokens for the ebnf
 */
using System;
using System.Collections.Generic;

namespace EbnfStudio
{

    /// <summary>
    /// possible errors
    /// </summary>
    [Flags]
    public enum TokenError
    {
        NoError             = 0,
        Unknown             = 1 << 1,
        StringNotClosed     = 1 << 2,
        MissingIdentifier   = 1 << 3,
        MissingBrace_Open   = 1 << 4,
        MissingBrace_Close  = 1 << 5,
        MissingBracket_Open = 1 << 6,
        MissingBracket_Close= 1 << 7,
        MissingParen_Open   = 1 << 8,
        MissingParen_Close  = 1 << 9,
        MissingIdent_Begin  = 1 << 10,
        MissingIdent_End    = 1 << 11,
        MissingAssignment   = 1 << 12,
        InvalidInput        = 1 << 13
    }


    /// <summary>
    /// Token classes
    /// </summary>
    public enum TokenClass
    {
        Invalid, Comment,               // invalid, the comment
        Brace_Open, Brace_Close,        // {, }
        Bracket_Open, Bracket_Close,    // [, ]
        Paren_Open, Paren_Close,        // (, )
        Assign, Pipe,                   // =, |
        Identifier, Literal,            // hello, "hello", 'hello'
        Empty,                          // ε - empty production
        EndOfFile, EndOfStatement,      // end of the file, enf od teh statement., ;
    }


    /// <summary>
    /// Represent a single token read from the input stream
    /// </summary>
    public class Token
    {

        // Token ID
        public TokenClass Id { get; set; }

        // Set or get token error code
        public TokenError ErrorCode { get; set; }

        // Get error code as a string message
        public List<string> Errors { get { return Token.GetErrorName(this.ErrorCode); } }

        // Token lexeme
        public string Lexeme { get; set; }

        // line number
        public int Line { get; set; }

        // line column
        public int Column { get; set; }

        // real size in chars of the token
        public int Size { get; set; }

        // additional data used per class
        public int Data { get; set; }


        /// <summary>
        /// Get token name
        /// </summary>
        public static string GetClassName(TokenClass id)
        {
            switch (id)
            {
                case TokenClass.Brace_Open       : return "{";
                case TokenClass.Brace_Close      : return "}";
                case TokenClass.Bracket_Open     : return "[";
                case TokenClass.Bracket_Close    : return "]";
                case TokenClass.Paren_Open       : return "(";
                case TokenClass.Paren_Close      : return ")";
                case TokenClass.Assign           : return "=";
                case TokenClass.Pipe             : return "|";
                case TokenClass.Empty            : return "''";
                case TokenClass.Identifier       : return "Identifier";
                case TokenClass.Literal          : return "Literal";
                case TokenClass.EndOfStatement   : return "End-Of-Statement";
                case TokenClass.EndOfFile        : return "End-Of-File";
            }
            return "Invalid-Token-Class-" + id.ToString();
        }


        /// <summary>
        /// Get message for the given error code
        /// </summary>
        private static List<string> GetErrorName(TokenError error)
        {
            var msgs = new List<string>();

            if ((error & TokenError.Unknown) != 0) msgs.Add("Unknown error");
            if ((error & TokenError.StringNotClosed) != 0) msgs.Add("String not closed");
            if ((error & TokenError.MissingIdentifier) != 0) msgs.Add("Missing identifier");
            if ((error & TokenError.MissingBrace_Open) != 0) msgs.Add("Missing {");
            if ((error & TokenError.MissingBrace_Close) != 0) msgs.Add("Missing }");
            if ((error & TokenError.MissingBracket_Open) != 0) msgs.Add("Missing [");
            if ((error & TokenError.MissingBracket_Close) != 0) msgs.Add("Missing ]");
            if ((error & TokenError.MissingParen_Open) != 0) msgs.Add("Missing (");
            if ((error & TokenError.MissingParen_Close) != 0) msgs.Add("Missing )");
            if ((error & TokenError.MissingIdent_Begin) != 0) msgs.Add("Missing <");
            if ((error & TokenError.MissingIdent_End) != 0) msgs.Add("Missing >");
            
            return msgs;
        }


        /// <summary>
        /// Get full lexeme
        /// </summary>
        public override string ToString()
        {
            if (Id == TokenClass.Identifier && Data == 1)
                return "<" + Lexeme + ">";
            return Lexeme;
        }


        /// <summary>
        /// Construct from id, lexeme, line, column and error
        /// </summary>
        public Token(TokenClass id, string lexeme, int line, int column, int size, TokenError error)
        {
            Id = id; Lexeme = lexeme; Line = line; Column = column; Data = 0;
            Size = size; ErrorCode = error;
        }


        /// <summary>
        /// Construct from id, lexeme, line and column
        /// </summary>
        public Token(TokenClass id, string lexeme, int line, int column, int size)
            : this(id, lexeme, line, column, size, TokenError.NoError)
        {
        }


        /// <summary>
        /// Construct id, line and column
        /// </summary>
        public Token(TokenClass id, int line, int column, int size)
            : this(id, GetClassName(id), line, column, size, TokenError.NoError)
        {
        }

    }
}
